package com.tim.test.leetcode.algorithm;

import com.alibaba.fastjson.JSON;

import java.util.Random;

public class SortMethod {

    public static void main(String[] args) {
        int[] arr = new Random().ints(20, 0, 200).toArray();
//        arr = IntStream.range(1, 10).toArray();
        System.out.println(JSON.toJSONString(arr));
//      bubbleAsc(arr);
        fastAsc(arr, 0, arr.length-1);
//        quickSort(arr, 0, arr.length-1);
        System.out.println(JSON.toJSONString(arr));
    }

    /**
     * 冒泡排序
     *  两次遍历，交换移动；
     *  times辅助判断排序结束
     */
    public static void bubbleAsc(int[] nums){
        final int length = nums.length;
        if(nums==null || length <2){
            return;
        }
        for (int i = 0; i < length; i++) {
            int times = 0;
            for (int j= 0; j < length-i-1; j++) {
                if(nums[j]>nums[j+1]){
                    int temp = nums[j];
                    nums[j] = nums[j+1];
                    nums[j+1] = temp;
                    times++;
                }
            }
            if(times==0){
                break;
            }
        }
    }

    /**
     * 快速排序
     *  临时变量交换移动；记录基准点位置
     *  迭代1
     */
    public static void fastAsc(int[] nums, int startIndex, int endIndex){
        if(startIndex>=endIndex){
            return ;
        }

//        int index = singlePointer(nums, startIndex, endIndex);
        int index = doublePointerSwap(nums, startIndex, endIndex);
        fastAsc(nums, startIndex, index-1);
        fastAsc(nums, index+1, endIndex);

    }

    /**
     * 单边指针
     * 思路：
     * 记录首元素为分界值 pivot, 创建标记 mark 指针。
     * 循环遍历与分界值对比。
     * 比分界值小，则 mark++ 后与之互换。
     * 结束循环后，将首元素分界值与当前mark互换。
     * 返回 mark 下标为分界值下标。
     */
    private static int singlePointer(int[] nums, int left, int right) {
        int markIndex = left;
        int mVal = nums[left];
        int i=left;
        while(i<right){
            i++;
            if(nums[i]<mVal){
                markIndex++;
                swap(nums, markIndex, i);
            }
        }
        if(markIndex!=left){
            swap(nums, markIndex, left);
        }
        return markIndex;
    }

    private static void swap(int[] nums, int i, int j){
        if(i==j){
            return;
        }
        int temp = nums[j];
        nums[j] = nums[i];
        nums[i] = temp;
    }
        /**
         * 双边指针（交换法）
         * 思路：
         * 记录分界值 pivot，创建左右指针（记录下标）。
         * （分界值选择方式有：首元素，随机选取，三数取中法）
         *
         * 首先从右向左找出比pivot小的数据，
         * 然后从左向右找出比pivot大的数据，
         * 左右指针数据交换，进入下次循环。
         *
         * 结束循环后将当前指针数据与分界值互换，
         * 返回当前指针下标（即分界值下标）
         */
    private static int doublePointerSwap(int[] arr, int startIndex, int endIndex) {
        int mVal = arr[startIndex];
        int i=startIndex, j=endIndex;
        while(i<j){
            while(i<j && arr[j]>=mVal){
                j--;
            }
            while(i<j && arr[i]<=mVal){
                i++;
            }
            swap(arr, i, j);
        }
        if(i!=startIndex){
            swap(arr, i, startIndex);
        }
        return i;
    }



}


